﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;
using System.IO;
using StorageAbstraction;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.Configuration;

namespace StorageAbstraction
{
    public class StorageSubsystem
    {
        #region fields
        private string _systemContainerName;
        private string _requestContainerName;
        private string _responseContainerName;
        private string _requestQueueName;
        private string _responseQueueName;
        private string _currentProcessorBlobName;
        private CloudBlobContainer _systemContainer;
        private CloudBlobContainer _requestContainer;
        private CloudBlobContainer _responseContainer;
        private CloudStorageAccount _account;
        private CloudBlobClient _bClient;
        private CloudQueueClient _qClient;
        private StdQueue<VideoTaskMessage> _reqQueue;
        private StdQueue<VideoTaskMessage> _respQueue;
        #endregion

        #region ctor
        public StorageSubsystem(string connString,string reqQName, string respQName, string reqCName, string respCName, string sysCName)
        {
            this._systemContainerName = sysCName;
            this._requestContainerName = reqCName;
            this._responseContainerName = respCName;
            this._requestQueueName = reqQName;
            this._responseQueueName = respQName;
          
            this.Initialize(connString);
        }
        #endregion

        #region Methods
        #region Initialize
        internal void Initialize(string connString)
        {
            this._account = CloudStorageAccount.Parse(connString);
            this._bClient = this._account.CreateCloudBlobClient();
            this.InitializeBlobs();
            this.InitializeQueues();
        }
        #endregion

        #region InitializeBlobs
        private void InitializeBlobs()
        {
            #region Initialize SystemContainer
            _systemContainer = this._bClient.GetContainerReference(this._systemContainerName);
            _systemContainer.CreateIfNotExist();

            var permissions = _systemContainer.GetPermissions();
            permissions.PublicAccess = BlobContainerPublicAccessType.Off;
            _systemContainer.SetPermissions(permissions);
            #endregion

            #region Initialize RequestContainer
            this._requestContainer = this._bClient.GetContainerReference(this._requestContainerName);
            this._requestContainer.CreateIfNotExist();

            var requestPermissions = this._requestContainer.GetPermissions();
            requestPermissions.PublicAccess = BlobContainerPublicAccessType.Off;
            this._requestContainer.SetPermissions(requestPermissions);
            #endregion

            #region Initialize ResponseContainer
            this._responseContainer = this._bClient.GetContainerReference(this._responseContainerName);
            this._responseContainer.CreateIfNotExist();

            var responsePermissions = this._responseContainer.GetPermissions();
            responsePermissions.PublicAccess = BlobContainerPublicAccessType.Off;
            this._responseContainer.SetPermissions(responsePermissions);
            #endregion
        }
        #endregion

        #region InitializeQueues
        private void InitializeQueues()
        {
            this._qClient = this._account.CreateCloudQueueClient();

            var requestQueue = this._qClient.GetQueueReference(this._requestQueueName);
            requestQueue.CreateIfNotExist();
            this._reqQueue = new StdQueue<VideoTaskMessage>(requestQueue);

            var responseQueue = this._qClient.GetQueueReference(this._responseQueueName);
            responseQueue.CreateIfNotExist();
            this._respQueue = new StdQueue<VideoTaskMessage>(responseQueue);
        }
        #endregion

        #region UpdateCurrentProcessor
        private void UpdateCurrentProcessor()
        {
            this._currentProcessorBlobName = RoleEnvironment.GetConfigurationSettingValue("CurrentProcessorBlob");
        }
        #endregion

        #region GetProcessor
        public string GetProcessor()
        {
            this.UpdateCurrentProcessor();
            CloudBlob processorBlob = this._systemContainer.GetBlobReference(this._currentProcessorBlobName);
            string fileName = System.IO.Path.GetTempFileName() + ".exe";
            processorBlob.DownloadToFile(fileName);
            return fileName;
        }
        #endregion

        #region SaveOutputFile
        public void SaveOutputFile(byte[] fileContent, string fileName, Guid taskID)
        {
            var blob = this._responseContainer.GetBlobReference(fileName);
            blob.Properties.ContentType = "video/x-flv";
            blob.UploadByteArray(fileContent);
            VideoTaskMessage vmsg = new VideoTaskMessage();
            vmsg.TaskDateTime = DateTime.Now;
            vmsg.BlobName = fileName;
            vmsg.TaskID = taskID;
            this._respQueue.AddMessage(vmsg);
        }
        #endregion

        #region TryGetTask
        public bool TryGetTask(out VideoTaskMessage vmsg)
        {
            bool res = false;
            vmsg = null;
            CloudQueueMessage msg = this._reqQueue.GetMessage();
            if (msg != null)
            {
                vmsg = QueueMessageBase.FromMessage<VideoTaskMessage>(msg);
                //detele the message after getting it
                this._reqQueue.DeleteMessage(msg);
                res = true;
            }
            return res;
        }
        #endregion

        #region DownloadRequestFile
        public void DownloadRequestFile(string blobName, FileStream fs)
        {
            var reqBlob = this._requestContainer.GetBlobReference(blobName);
            reqBlob.DownloadToStream(fs);
        }
        #endregion

        #region UploadRequestFile
        public Guid UploadRequestFile(FileStream fs, string FileName)
        {
            var reqBlob = this._requestContainer.GetBlobReference(FileName);
            reqBlob.UploadFromStream(fs);
            VideoTaskMessage vmsg = new VideoTaskMessage();
            vmsg.BlobName = FileName;
            vmsg.TaskID = Guid.NewGuid();
            vmsg.TaskDateTime = DateTime.Now;
            this._reqQueue.AddMessage(vmsg);
            return vmsg.TaskID;
        }
        #endregion

        #region GetTaskCompleteMessage
        public VideoTaskMessage GetTaskCompleteMessage(Guid taskID)
        {
            VideoTaskMessage vmsg = null;
            CloudQueueMessage msg = this._respQueue.GetMessage();
            if (msg != null && msg.DequeueCount > 10)
            {
                // something wrong with that message
                // consider logging it and delete it
                this._respQueue.DeleteMessage(msg);
                msg = null;
            }
            while (msg != null && msg.DequeueCount < 10)
            {
                VideoTaskMessage tmpVmsg = QueueMessageBase.FromMessage<VideoTaskMessage>(msg);
                if (tmpVmsg.TaskID.Equals(taskID))
                {
                    vmsg = tmpVmsg;
                    this._respQueue.DeleteMessage(msg);
                    break;
                }
                msg = this._respQueue.GetMessage();
            }
            return vmsg;
        }
        #endregion

        #region DownloadTaskResult
        public void DownloadTask(VideoTaskMessage vmsg, string path)
        {
            if (vmsg != null)
            {
                var blob = this._responseContainer.GetBlobReference(vmsg.BlobName);
                using (FileStream fs = File.Create(string.Format("{0}\\{1}", path, vmsg.BlobName)))
                {
                    blob.DownloadToStream(fs);
                }
            }
        }
        #endregion

        #endregion
    }
}
